#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

#include "help_api.h"
#include "memory_map_file.h"

int MemoryMappedFile::open_temp_file(){
    char temp_filename[15] = "";
	int sfp;
	int fd = -1;
	 
	strncpy(temp_filename, "/tmp/fi.XXXXXX", sizeof (temp_filename));
	if ((fd = mkstemp(temp_filename)) == -1 ||
		(sfp = open(temp_filename, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600))==0 ) {
		  if (fd != -1) {
		    unlink(temp_filename);
		    close(fd);
		  }
	   error_print("memory_map_file.cpp: failed creating file%s\n", temp_filename);
	   throw 0;
	}
    this->temp_filename = temp_filename;
	return (sfp);
}

MemoryMappedFile::MemoryMappedFile(string filename, uint file_size, uint address, bool deleteAfterUse){
    this->deleteAfterUse = deleteAfterUse;
    int fd;
    fd = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
    if(fd==0)
    {
        error_print("memory_map_file.cpp: failed opening file %s\n", filename.c_str());
        throw 0;
    }
    this->temp_filename = filename;
    init(fd,file_size, address);
}

MemoryMappedFile::MemoryMappedFile(uint file_size, uint address){
    deleteAfterUse = true;
    /* Open a file for writing. */
	fd = (int)open_temp_file();
    init(fd, file_size, address);
}

void MemoryMappedFile::init(int fd,uint file_size, uint address){
    int result;
    this->file_size = file_size;
    this->address = address;	

       
    /* Stretch the file size to the size of the (mmapped) array of ints
     */
    result = lseek(fd, file_size-1, SEEK_SET);
    if (result == -1) {
		close(fd);
		error_print("memory_map_file.cpp: Error calling lseek() to 'stretch' the file\n");
		throw 0;
    }
    
    /* Something needs to be written at the end of the file to
     * have the file actually have the new size.
     * Just writing an empty string at the current file position will do.
     *
     * Note:
     *  - The current position in the file is at the end of the stretched 
     *    file due to the call to lseek().
     *  - An empty string is actually a single '\0' character, so a zero-byte
     *    will be written at the last byte of the file.
     */
    result = write(fd, "", 1);
    if (result != 1) {
		close(fd);
		error_print("memory_map_file.cpp: Error writing last byte of the file\n");
		throw 0;
    }

    /* Now the file is ready to be mmapped.
     */
    debug_print("memory_map_file.cpp: file_size to mmap is %02X\n", file_size);
    map = mmap((void*)address, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
		close(fd);
		error_print("memory_map_file.cpp: Error mmapping the file\n");
		throw 0;
    }
    debug_print("memory_map_file.cpp: mmap address : %08X\n", (int)map);
}

MemoryMappedFile::~MemoryMappedFile(){
    /* Don't forget to free the mmapped memory
     */
    if (munmap(map, file_size) == -1) {
		error_print("memory_map_file.cpp: Error un-mmapping the file");
		throw 0;
    }

    /* Un-mmaping doesn't close the file, so we still need to do that.
     */
    close(fd);
    if(deleteAfterUse)
    {
        debug_print("Deleting temp file %s\n", temp_filename.c_str());
        unlink(temp_filename.c_str());
    }
}

 